/*
 *    Copyright © OpenAtom Foundation.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */

package com.inspur.edp.cef.repository.adaptoritem;

import com.inspur.edp.cef.api.RefObject;
import com.inspur.edp.cef.api.repository.GspDbDataType;
import com.inspur.edp.cef.api.repository.INestedRepository;
import com.inspur.edp.cef.api.repository.IRootRepository;
import com.inspur.edp.cef.api.repository.ITypeTransProcesser;
import com.inspur.edp.cef.api.repository.readerWriter.ICefReader;
import com.inspur.edp.cef.entity.condition.EntityFilter;
import com.inspur.edp.cef.entity.entity.IEntityData;
import com.inspur.edp.cef.repository.adaptor.EntityRelationalAdaptor;
import com.inspur.edp.cef.repository.exception.CefRepositoryException;
import com.inspur.edp.cef.spi.repository.IAdaptorItem;
import com.inspur.edp.cef.repository.adaptor.KeyWordsManager;
import com.inspur.edp.cef.repository.assembler.AssoCondition;
import com.inspur.edp.cef.repository.assembler.AssociationInfo;
import com.inspur.edp.cef.repository.assembler.LogicDeleteInfo;
import com.inspur.edp.cef.repository.dbcolumninfo.DbColumnInfo;
import com.inspur.edp.cef.repository.dbcolumninfo.DbColumnInfoCollection;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public abstract class AdaptorItem implements IAdaptorItem {
    //entity extend dynamicattr
    protected static Logger logger = LoggerFactory.getLogger(EntityRelationalAdaptor.class);
    protected HashMap<String, Integer> mapping = new HashMap<>();
    protected HashMap<Integer, String> insertMultiIndexMap = new HashMap<>();
    private java.util.ArrayList<AssociationInfo> associationInfos;
    private HashMap<String, INestedRepository> nestedRepositories = new HashMap<>();
    private final Object netstRepositoryLock = new Object();


    public AdaptorItem(){
    }

    protected AdaptorItemSqlCache getSqlCache(){
        return null;
    }
    protected abstract void initColumns();
    protected abstract void initAssociations();
    protected final HashMap<String, Integer> getMapping(){
        return mapping;
    }
    public abstract Object readProperty(String propertyName, ICefReader reader);
    protected abstract void setEntityValue(ICefReader reader, IEntityData entityData);
    protected INestedRepository getNestedRepository(String configId){
        if(!nestedRepositories.containsKey(configId)){
            synchronized (netstRepositoryLock){
                if(!nestedRepositories.containsKey(configId)){
                    nestedRepositories.put(configId, com.inspur.edp.udt.api.UdtManagerUtils.getUdtRepositoryFactory().createRepository(configId));
                }
            }
        }
        return nestedRepositories.get(configId);
    }

    public boolean isMainAdapterItem()
    {return false;}

    @Override
    public INestedRepository getNestedRepo(String configId)
    {
        return getNestedRepository(configId);
    }
    public Map<Integer, String> getInsertMultiIndexMap(){
        return insertMultiIndexMap;
    }
    public DbColumnInfoCollection getContainColumns() {
        return null;
    }

    public abstract String getTableAlias();
    public abstract void setTableAlias(String value);

    public LogicDeleteInfo getLogicDeleteInfo(){
        return new LogicDeleteInfo(false, "");
    }

    public final void addColumn(String columnName, String dbColumnName, GspDbDataType columnType, int length, int precision, Object defaultValue
            , ITypeTransProcesser typeTransProcesser, boolean isPrimaryKey, boolean isAssociateRefElement, boolean isMultiLang, boolean isUdt
            , boolean isAssociation, boolean isEnum, String belongElementLabel, boolean isParentId) {
        if(!getContainColumns().contains(columnName)){
            synchronized (getContainColumns()){
                if(!getContainColumns().contains(columnName)){
                    DbColumnInfo tempVar = new DbColumnInfo();
                    tempVar.setColumnName(columnName);
                    tempVar.setDbColumnName(isAssociateRefElement ? "" : dbColumnName);
                    tempVar.setColumnType(columnType);
                    tempVar.setLength(length);
                    tempVar.setPrecision(precision);
                    tempVar.setDefaultValue(defaultValue);
                    tempVar.setIsPrimaryKey(isPrimaryKey);
                    tempVar.setIsAssociateRefElement(isAssociateRefElement);
                    tempVar.setIsMultiLang(isMultiLang);
                    tempVar.setIsParentId(isParentId);
                    tempVar.setIsUdtElement(isUdt);
                    tempVar.setIsAssociation(isAssociation);
                    tempVar.setIsEnum(isEnum);
                    tempVar.setBelongElementLabel(belongElementLabel);
                    tempVar.setTypeTransProcesser(typeTransProcesser);
                    DbColumnInfo columnInfo = tempVar;
                    getContainColumns().add(columnInfo);
                }
            }
        }
    }

    public final void addColumn(DbColumnInfo dbColumnInfo) {
        if(!getContainColumns().contains(dbColumnInfo.getColumnName())){
            synchronized (getContainColumns()){
                if(!getContainColumns().contains(dbColumnInfo.getColumnName())){
                    getContainColumns().add(dbColumnInfo);
                }
            }
        }
    }

    protected java.util.ArrayList<AssociationInfo> getAssociationInfos() {
        if (associationInfos == null)
            associationInfos = new ArrayList<AssociationInfo>();
        return associationInfos;
    }

    @Override
    public final AssociationInfo getAssociation(String propertyName) {
        for (AssociationInfo item : getAssociationInfos()) {
            if (propertyName.equals(item.getSourceColumn())) {
                return item;
            }
        }
        throw new CefRepositoryException("找不到"+propertyName+"属性对应的关联信息。");
    }

    protected final void addAssociationInfo(com.inspur.edp.cef.spi.entity.AssociationInfo associationInfo, IRootRepository refRepository, java.util.HashMap<String, String> refColumns) {

        AssociationInfo tempVar = new AssociationInfo();
        tempVar.setNodeCode(associationInfo.getNodeCode());
        tempVar.setSourceColumn(associationInfo.getPrivateTargetColumn());
        tempVar.setTargetColumn(associationInfo.getPrivateSourceColumn());
        tempVar.setRefRepository(refRepository);
        tempVar.setRefColumns(refColumns);
        tempVar.setConfigId(associationInfo.getConfig());

        tempVar.setWhere(associationInfo.getWhere());
        if(associationInfo.getAssoConditions() != null && associationInfo.getAssoConditions().size() > 0){
            ArrayList<AssoCondition> assoConditions = new ArrayList<>();
            for(com.inspur.edp.cef.spi.entity.AssoCondition assoCondition : associationInfo.getAssoConditions()){
                AssoCondition condi = new AssoCondition();
                condi.setLeftNodeCode(assoCondition.getLeftNodeCode());
                condi.setLeftField(assoCondition.getLeftField());
                condi.setLeftElemenetId(assoCondition.getLeftElemenetId());
                condi.setOperator(assoCondition.getOperator());
                condi.setRightNodeCode(assoCondition.getRightNodeCode());
                condi.setRightField(assoCondition.getRightField());
                condi.setRightElementId(assoCondition.getRightElementId());
                condi.setValue(assoCondition.getValue());

                assoConditions.add(condi);
            }
            tempVar.setAssoConditions(assoConditions);
        }


        AssociationInfo info = tempVar;
        getAssociationInfos().add(info);
    }

    protected final void addAssociationInfo(String nodeCode, String sourceColumn, String targetColumn, IRootRepository refRepository, java.util.HashMap<String, String> refColumns, String configId) {

        AssociationInfo tempVar = new AssociationInfo();
        tempVar.setNodeCode(nodeCode);
        tempVar.setSourceColumn(sourceColumn);
        tempVar.setTargetColumn(targetColumn);
        tempVar.setRefRepository(refRepository);
        tempVar.setRefColumns(refColumns);
        tempVar.setConfigId(configId);

        AssociationInfo info = tempVar;
        getAssociationInfos().add(info);
    }

    protected final void addAssociationInfo(String nodeCode, String sourceColumn, String targetColumn, IRootRepository refRepository, java.util.HashMap<String, String> refColumns, String configId, ArrayList<AssoCondition> list) {

        AssociationInfo tempVar = new AssociationInfo();
        tempVar.setNodeCode(nodeCode);
        tempVar.setSourceColumn(sourceColumn);
        tempVar.setTargetColumn(targetColumn);
        tempVar.setRefRepository(refRepository);
        tempVar.setRefColumns(refColumns);
        tempVar.setConfigId(configId);
        tempVar.setAssoConditions(list);
        AssociationInfo info = tempVar;
        getAssociationInfos().add(info);
    }


    protected String buildQueryFields(EntityFilter entityFilter, HashMap<String, Integer> mapping){
        StringBuilder columns = new StringBuilder();
        boolean hasPropDBIndexMapping = false;
        if(mapping == null){
            mapping = new HashMap<String, Integer>();
        }
        boolean usePropertyFilter = false;
        List<String> filterProperties = null;
        if (entityFilter != null && entityFilter.getFieldsFilter() != null && entityFilter.getFieldsFilter().isUseFieldsCondition()){
            usePropertyFilter = true;
            filterProperties = entityFilter.getFieldsFilter().getFilterFields();
        }
        int index = mapping == null ? 0 : mapping.size();
        index = buildQueryColumns(usePropertyFilter,filterProperties,getContainColumns(), columns, mapping, hasPropDBIndexMapping, index);
        if(!usePropertyFilter){
            this.mapping = mapping;
        }
        return columns.toString();
    }

    private int buildQueryColumns(boolean usePropertyFilter, List<String> filterProperties,
                                  DbColumnInfoCollection columnCollection,
                                  StringBuilder columns,
                                  HashMap<String, Integer> mapping,
                                  boolean hasPropDBIndexMapping,
                                  int index) {
        boolean containColumns = columns.length() > 0;
        if (usePropertyFilter) {
            for (DbColumnInfo containColumn : columnCollection) {
                if (filterProperties.contains(containColumn.getColumnName()) == false)
                {
                    if(containColumn.getIsAssociateRefElement()&&filterProperties.contains(containColumn.getBelongElementLabel()))
                    {}
                    if(containColumn.getBelongElementLabel()!=null&&containColumn.getBelongElementLabel().isEmpty()==false&&filterProperties.contains(containColumn.getBelongElementLabel()))
                    {}
                    else
                        continue;
                }

                if (containColumns) {
                    columns.append(",");
                }
                columns.append(trans2DbColumnWithAlias(containColumn.getColumnName())).append(" AS ").append(containColumn.getAliasName(index));
                containColumns = true;
                if (hasPropDBIndexMapping == false) {
                    mapping.put(containColumn.getColumnName(), index++);
                }
            }
        } else {
            for (DbColumnInfo containColumn : columnCollection) {
                if (containColumns) {
                    columns.append(",");
                }
                columns.append(trans2DbColumnWithAlias(containColumn.getColumnName())).append(" AS ").append(containColumn.getAliasName(index));
                containColumns = true;
                if (hasPropDBIndexMapping == false) {
                    mapping.put(containColumn.getColumnName(), index++);
                }
            }
        }
        return index;
    }

    private String trans2DbColumnWithAlias(String filedName) {
        DbColumnInfo columnInfo = getContainColumns().getItem(filedName);
        if (columnInfo.getIsAssociateRefElement()) {
            return getAssociateDbColumnName(columnInfo);
        }

        String dbName = KeyWordsManager.getTableAlias(getTableAlias()) + "." + KeyWordsManager.getColumnAlias(columnInfo.getDbColumnName());
        if (columnInfo.getIsMultiLang()) {
            dbName = dbName + "@Language@";
        }
        return dbName;
    }

    private String getAssociateDbColumnName(DbColumnInfo columnInfo) {
        if (columnInfo.getDbColumnName() == null || columnInfo.getDbColumnName().length() == 0) {
            throw new CefRepositoryException("尚未获取关联引用字段数据库列名称，请先获取引用字段");
        }
        return columnInfo.getDbColumnName();
    }
    public abstract String getQueryFields(EntityFilter entityFilter, HashMap<String, Integer> mapping);
    public String getQueryFieldsWithOutCache(EntityFilter entityFilter, HashMap<String, Integer> mapping){
        return "";
    };
    public abstract String getInsertFields(boolean isMultiData);
    protected String getInsertFields(){
        StringBuilder fields = new StringBuilder();
        for (DbColumnInfo containColumn : getContainColumns().getBaseDict().values()) {
            if (containColumn.getIsAssociateRefElement()) {
                continue;
            }
            if(containColumn.isVirtual())
                continue;
            if (fields.length() > 0)
                fields.append(",");
            String dbCol = KeyWordsManager.getColumnAlias(containColumn.getDbColumnName());
            if (containColumn.getIsMultiLang()) {
                dbCol = dbCol + "@Language@";
            }
            fields.append(dbCol);
        }
        return fields.toString();
    }
    protected abstract String getInsertValues(boolean isMultiData, RefObject<Integer> valuesCount);
    protected String getInsertValues(RefObject<Integer> valuesCount){
        StringBuilder values = new StringBuilder();
        for (DbColumnInfo containColumn : getContainColumns()) {
            if (containColumn.getIsAssociateRefElement()) {
                continue;
            }
            if(containColumn.isVirtual())
                continue;
            if (valuesCount.argvalue > 0)
                values.append(",");
            values.append("?");
            valuesCount.argvalue = valuesCount.argvalue + 1;
        }
        return values.toString();
    }
}
